package edu.ustb.efws.base.controller;

import edu.ustb.efws.BaseException;
import edu.ustb.efws.EfwsProperties;
import edu.ustb.efws.base.controller.form.TableDropForm;
import edu.ustb.efws.controller.vo.OkVo;
import edu.ustb.efws.controller.vo.ResultVo;
import edu.ustb.efws.pipeline.service.IptLogService;
import edu.ustb.efws.pipeline.service.RiskLogService;
import edu.ustb.efws.pipeline.service.ThosLogService;
import edu.ustb.efws.security.Credential;
import edu.ustb.efws.security.CredentialContextUtils;
import edu.ustb.efws.utils.DateUtils;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.validation.Valid;
import java.time.Duration;
import java.time.LocalDate;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 表管理
 *
 * @author TS Group
 */
@RestController
@RequestMapping("/manage/table")
@Api(value = "/manage/table", tags = "日志表管理")
public class TableController {
    private static final Logger LOGGER = LoggerFactory.getLogger(TableController.class);

    private static final long MAX_WAIT_MILLS = Duration.ofMinutes(30).toMillis();

    private final IptLogService iptLogService;
    private final RiskLogService riskLogService;
    private final ThosLogService thosLogService;
    private final AtomicLong dropTime;
    private final EfwsProperties properties;

    @Autowired
    public TableController(IptLogService iptLogService, RiskLogService riskLogService,
                           ThosLogService thosLogService, EfwsProperties properties) {

        this.iptLogService = iptLogService;
        this.riskLogService = riskLogService;
        this.thosLogService = thosLogService;
        this.properties = properties;
        this.dropTime = new AtomicLong(-1L);
    }

    @PostMapping(value = "drop", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE)
    @ApiOperation("删除日志表")
    public ResultVo<OkVo> drop(@Valid @RequestBody TableDropForm form){
        if(form.getFromMonth() >= form.getToMonth()){
            throw new BaseException("开始时间需要小于结束时间");
        }

        LocalDate localDate = DateUtils
                .monthNumToLocalData(form.getToMonth())
                .plusMonths(properties.getMinLogDataMonth());
        
        if(localDate.compareTo(LocalDate.now()) >= 0){
            throw new BaseException(String.format("只能删除%d个月前数据", properties.getMinLogDataMonth()));
        }

        if(hasDrop()){
            throw new BaseException("已有删除任务，请稍后执行");
        }

        Credential credential = CredentialContextUtils.getCredential();
        String remark = String.format("%d-%s 用户 删除", credential.getId(), credential.getUsername());
        new Thread(() -> {

            LOGGER.info("Start drop partition from={}, to={}", form.getFromMonth(), form.getToMonth());
            long startTime = System.currentTimeMillis();
            iptLogService.dropPartition(form.getFromMonth(), form.getToMonth(), remark);
            riskLogService.dropPartition(form.getFromMonth(), form.getToMonth(), remark);
            thosLogService.dropPartition(form.getFromMonth(), form.getToMonth(), remark);
            LOGGER.info("End drop partition from={}, to ={}, useTime={}",
                    form.getFromMonth(), form.getToMonth(), System.currentTimeMillis() - startTime);

            dropTime.set(-1L);
        }).start();

        return ResultVo.success(new OkVo(true));
    }

    private boolean hasDrop(){
        long now = System.currentTimeMillis();
        if(dropTime.compareAndSet(-1, now)){
            return false;
        }

        if(dropTime.addAndGet(MAX_WAIT_MILLS) < now){
            dropTime.set(now);
            return false;
        }
        return true;
    }
}
